Weighted Nonlinear Fit

March 18, 2021

In this Jupyter notebook we will fit a nonlinear function to a set of experimental data. The fit will be weighted by the measurement uncertainties.

In this example, we take data from a series $LRC$ circuit. For the $y$-axis we will have the ratio of the magntidue of the voltage across the resistance to that supplied by the function generator driving the circuit ($V_\mathrm{ratio}=V_R/V_0$). On the x-axis we will have angular frequency $\omega$.

The process will be very similar to the process used to fit a linear function to a dataset. The main difference will be in the definition of the model function that we are fitting to.

To do the actual fit, we will use the curve_fit() function from the SciPy module. This way of fitting is very nice because we use it for all types of fit models (linear, polynomial, linear-in-parameter fits, and nonlinear fits). It is capable of doing both unweighted and weighted fits and it will return uncertainties in the fit parameters via the covariance matrix.

The first step is to define a function for the model that we will fit our data to. In this case, the model is a Lorentzian:

$V_\mathrm{ratio}=\frac{A}{\sqrt{1+\left(\dfrac{\omega}{\gamma}\right)^2\left[1-\left(\dfrac{\omega_0}{\omega}\right)^2\right]^2}}$

$\omega_0$ is the resonance frequency
$A$ is the amplitude (height of the resonance peak)
$\gamma$ is the width of the resonance
$\omega$ is the independent variable along the horizontal axis (angular frequency in this case)

Here is the actual command to execute the fit. At a minimum, curve_fit() requires as inputs the function that defines the model, the x-data, and the y-data. The statement below tells curve_fit() to return a list of the best-fit parameters and the covariance matrix which will be used to determine the error in the fit parameters.

To give the fit a chance at being successful, we have to provide reasonable initial guesses for the fit parameters. We use the option "p0" for the list of starting parameters. The order must be the same as the order of the parameters defined in the function "LRCFunc()".

The uncertainties of the best-fit parameters are determined from the square roots of the diagonal elements of the covariance matrix.

We can select the diagonal elements using:

np.diag()[0] for the (0,0) element
np.diag()[1] for the (1,1) element
np.diag()[2] for the (2,2) element

Note the use of the unicode character \u0394 to print $\Delta$.

We can now report:

$A = 0.722\pm 0.004$
$\gamma=68000\pm 1000~\mathrm{s}^{-1}$
$\omega_0=213300\pm 400~\mathrm{s}^{-1}$

All of this has produced an "unweighted" fit to the data. To include weights, all we need to do is include another option in curve_fit(). Everything else is exactly the same!

The new option is "sigma" and it is simply a list of the errors in the y-values ($\Delta V_\mathrm{ratio}$ in the case of this example). Note that many fitting routines require you to provide the actual weights as $1/\sigma^2$. That is not the case here. You just have to provide the absolute $y$-uncertainties. We will also specify that the errors are absolute errors with the option "absolute_sigma=True".

We can now report:

$A = 0.724\pm 0.004$
$\gamma=66800\pm 600~\mathrm{s}^{-1}$
$\omega_0=213900\pm 300~\mathrm{s}^{-1}$